#include "network.h"

/**
 * Handles network functions for p2p application.  
 * I.e., UDP Connection and communication
 *
 * Attributes: 
 * Some code referenced from Professor Edo Biagioni's ICS451 
 * TCP Client/Server code in Excercise 1.
 */


/**
 * Configures the host's sockaddr struct in preparation for the call to bind
 * 
 * @param port - port number to bind on
 */

struct sockaddr * configureHost (short port) {
	
	//Structs to hold network information
	static struct sockaddr * sap;
	static struct sockaddr_in sin;
	
	//Ensure struct is empty (all 0s)
	memset (&sin, 0, sizeof(sin));
	
	//Populate struct with port, family, and address to bind on
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr.s_addr = INADDR_ANY;  
	
	//Convert to sockaddr
	sap = (struct sockaddr *) &sin;
	
	return sap;
}


/**
 * Takes a port number and address as string arguments and populates
 * a sockaddr struct for using in network communication
 *
 * @param ip - ip address as a string.  E.g., "127.0.0.1" or "localhost" or "www.google.com"
 * @param portString - port number as a string
 */

struct sockaddr * inputConnectionInfo(char * address, unsigned short port) {
	
	//Struct to load IP address into sockaddr struct
	struct hostent * hostEntry;
	
	//Struct containing address family (e.g., IPv4)
	//also contains port and IP address.  Allocate memory since
	//struct will be returned
	struct sockaddr_in * sin = malloc (sizeof (struct sockaddr_in) );
	
	//Cast to sockaddr struct
	struct sockaddr * sap = (struct sockaddr *) sin;
	
	//Converts IP address to struct sockaddr_in
	hostEntry = gethostbyname(address);
	
	//Check if gethostbyname was successful or not
	if ( (hostEntry == NULL) || (hostEntry->h_addr_list == NULL) ) {
		error ("Invalid address given");
	}
	
	//Ensure that the struct that contains IP address is initialized to 0
	memset ( sin, 0, sizeof(*sin) );
	
	//Populate sockaddr_in struct
	sin->sin_family = AF_INET;
	sin->sin_port = htons(port);
	memcpy(&(sin->sin_addr), hostEntry->h_addr, hostEntry->h_length);
	
	return sap;
}

/**
 * Sends a message via UDP
 * 
 * @param address - string containing address (IP or hostname)
 * @param port - port number
 * @param socketfd - socket file descriptor
 * @param msg - msg to send (not necessarily a string)
 * @param msgLen - length of message in bytes
 */
void sendUDP(char * address, unsigned short port, int socketfd, unsigned char * msg, int msgLen) {
	
	//Populate struct with destination port and address
	struct sockaddr * destSockAddr = inputConnectionInfo(address, port);
	
	//Send message to destination address/port
	if (sendto(socketfd, msg, msgLen, 0, destSockAddr, sizeof(*destSockAddr)) < 0){
		error("sendto");
	}	
} // End sendUDP


/**
 * Receives a message via UDP
 * 
 * @param socketfd - socket file descriptor
 * @param buffer - buffer to read information into
 * @param bufferlen - size of buffer
 */
int receiveUDP(int socketfd, unsigned char * buffer, int bufferLen, struct sockaddr * senderSockAddr) {
	
	//Prepare parameter for recvfrom function.  Requires pointer to unsigned int
	unsigned int size = sizeof(*senderSockAddr);
	
	int bytesReceived;
	
	//Set to receive message on bound port and socket
	if((bytesReceived = recvfrom(socketfd, buffer, bufferLen, 0, senderSockAddr, &size)) < 0){
		error("recvfrom");
	}
	return bytesReceived;
	
} // End receiveUDP

/**
 * Prints a specific number of bytes in a given buffer in hexadecimal
 * used for debugging
 * 
 * @param buffer - buffer to be printed
 * @param byteCount - number of bytes to print
 */
void printMessage(unsigned char * buffer, int byteCount) {
	int i;
	for (i = 0; i < byteCount; i++){
		printf("%x ", buffer[i]);
	}
	printf("\n");
}

/**
 * Extracts port and IP address from a given sockaddr struct
 *
 * @param senderSockAddr - sockaddr returned from recvfrom function
 * @param addressPort - struct to hold address and port information
 */
void getAddressPort (struct sockaddr * senderSockAddr, struct addressPort * senderAddress) {
	
	//Cast as sockaddr_in - allows for easy access to IP address and port
	struct sockaddr_in * sin = (struct sockaddr_in *) senderSockAddr;
	
	//Assign address and port to struct holding data
	senderAddress->address = ntohl(sin->sin_addr.s_addr);  
	senderAddress->port = ntohs(sin->sin_port);
	
	//DEBUG - Prints address message it received from
	if (debug) {
		printf("RECEIVED FROM ADDRESS: %s\n", ipToString(senderAddress->address));
	}
	
}

/**
 * Converts a 4-byte IP address to a string
 * 
 * @param address - 4-byte IP address (unsigned int)
 * @return char pointer to string representation of IP address
 */

char * ipToString (unsigned int address){
	//Create in_addr struct for use with inet_ntoa()
	struct in_addr ia;
	
	//Populate in_addr with address
	ia.s_addr = htonl(address);
	
	//Return string representation - note: this is for IPv4 only
	return inet_ntoa(ia);
}


